#ifndef AVALON_HTTPSERVER_HPP
#define AVALON_HTTPSERVER_HPP
#include "Avalon/IO/Connection.hpp"
#include "Avalon/IO/ConnectionState.hpp"
#include "Avalon/SignalHandling/ConnectionGroup.hpp"
#include "Avalon/SignalHandling/TaskSignalHandler.hpp"
#include "Avalon/Threading/Async.hpp"
#include "Avalon/WebServices/WebServices.hpp"

namespace Avalon {
namespace WebServices {

  /*! \class HttpServer
   *  \brief Implements an HTTP server.
   *  \author Kamal Mansouri <kranar@hotmail.com>
   */
  class HttpServer : public IO::Connection {
    public:

      //! The default keep alive.
      static const boost::posix_time::time_duration KEEP_ALIVE;

      //! Signals an incoming HTTP request.
      /*!
        \param request The HTTP request.
        \param response The HTTP response.
      */
      typedef boost::signals2::signal<void (HttpServerRequest*,
        HttpServerResponse*)> HttpRequestSignal;

      //! Defines an HTTP request predicate.
      /*!
        \param request The request to test.
        \return <code>true</code> iff the <i>request</i> satisfies this
                predicate.
      */
      typedef boost::function<bool (const HttpServerRequest*)>
        HttpRequestPredicate;

      //! Constructs an HttpServer.
      /*!
        \param connection The ServerConnection accepting incoming connections.
        \param keepAliveTimer The Timer used to measure the keep alive.
        \param keepAlive The request keep alive duration.
        \param threadPool The ThreadPool used to handle requests.
      */
      HttpServer(IO::ServerConnection* connection,
        Threading::Timer* keepAliveTimer,
        boost::posix_time::time_duration keepAlive,
        Threading::ThreadPool& threadPool);

      virtual ~HttpServer();

      //! Starts the HttpServer.
      /*!
        \throw IOException if the server failed to start.
      */
      void Start();

      //! Receives HTTP requests where the request matches a predicate.
      /*!
        \param predicate The predicate to match the request against.
        \param slot The slot to connect.
        \return A connection receiving HTTP requests.
      */
      boost::signals2::connection ConnectRequestSignal(
        const HttpRequestPredicate& predicate,
        const HttpRequestSignal::slot_type& slot) const;

      virtual bool IsConnected() const;

      virtual void Close();

      virtual boost::signals2::connection ConnectClosedSignal(
        const ClosedSignal::slot_type& slot) const;

      virtual boost::signals2::connection ConnectClosedSignal(
        const ClosedSignal::slot_type& slot, Out<bool> isConnected) const;

    private:
      friend class HttpClientChannel;
      friend class HttpServerResponse;
      struct RequestSignalEntry {
        HttpRequestPredicate m_predicate;
        HttpRequestSignal m_signal;

        RequestSignalEntry(const HttpRequestPredicate& predicate);
      };
      mutable boost::mutex m_mutex;
      boost::scoped_ptr<IO::ServerConnection> m_connection;
      IO::ConnectionState m_connectionState;
      bool m_isConnected;
      bool m_isShuttingDown;
      std::vector<HttpClientChannel*> m_clients;
      SignalHandling::ConnectionGroup m_channelClosedConnections;
      mutable std::vector<RequestSignalEntry*> m_requestSignals;
      boost::scoped_ptr<Threading::Timer> m_keepAliveTimer;
      boost::posix_time::time_duration m_keepAlive;
      Threading::Async<IO::Channel*> m_acceptResult;
      Threading::Async<IO::Channel*>::State m_acceptState;
      Threading::Async<IO::Channel*>::FinishedSignal::slot_type m_acceptSlot;
      boost::condition_variable m_closedCondition;
      mutable ClosedSignal m_closedSignal;
      SignalHandling::TaskSignalHandler m_signalHandler;

      void Shutdown();
      void RemoveChannel(HttpClientChannel* channel);
      void SendResponse(HttpServerResponse* response,
        HttpClientChannel* channel);
      void OnConnectionAccepted();
      void OnConnectionClosed();
      void OnClientClosed(HttpClientChannel* channel);
      void OnClientRead(HttpClientChannel* channel);
  };
}
}

#endif // AVALON_HTTPSERVER_HPP
